package com.ideaaedi.hot.clazz.update.memory;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 传输内存容器
 *
 * @author JustryDeng
 * @since 2021/9/21 23:37:46
 */
public class MemoryTransferMap implements Runnable {
    
    /** 默认超时时间 */
    public static final long DEFAULT_TIMEOUT = 60L;
    
    /** 单例 */
    private final static MemoryTransferMap SINGLETON = new MemoryTransferMap();

    /** 针对热更新byte[]数据的全局内存容器 */
    private final Map<String, byte[]> globalMemory = new ConcurrentHashMap<>(64);
    
    /** globalMemory中键值对的计时器 */
    private final Map<String, Long> clock = new ConcurrentHashMap<>(64);
    
    /** 守护线程是否已经开始运行了 */
    private final AtomicBoolean daemonRunning = new AtomicBoolean(false);
    
    public synchronized static MemoryTransferMap globalMemory() {
        if (!SINGLETON.getDaemonRunning().get()) {
            //noinspection AlibabaAvoidManuallyCreateThread
            Thread thread = new Thread(SINGLETON);
            thread.setDaemon(true);
            thread.start();
            SINGLETON.getDaemonRunning().set(true);
        }
        return SINGLETON;
    }
    
    private MemoryTransferMap() {
    }
    
    /**
     * 键集合
     */
    public Set<String> keySet() {
        return MemoryTransferMap.SINGLETON.getGlobalMemory().keySet();
    }
    
    
    /**
     * 取
     */
    public byte[] get(String key) {
        return MemoryTransferMap.SINGLETON.getGlobalMemory().get(key);
    }
    
    /**
     * @see MemoryTransferMap#put(String, byte[], long)
     */
    public byte[] put(String key, byte[] value) {
        byte[] oldValue = MemoryTransferMap.SINGLETON.getGlobalMemory().put(key, value);
        clock.put(key, (System.currentTimeMillis() / 1000) + DEFAULT_TIMEOUT);
        return oldValue;
    }
    
    /**
     * 存
     *
     * @param key
     *         键
     * @param value
     *         值
     * @param timeout
     *         超时时长(单位秒)
     *
     * @return 旧value值
     */
    public byte[] put(String key, byte[] value, long timeout) {
        byte[] oldValue = MemoryTransferMap.SINGLETON.getGlobalMemory().put(key, value);
        clock.put(key, (System.currentTimeMillis() / 1000) + timeout);
        return oldValue;
    }
    
    
    @Override
    public void run() {
        //noinspection InfiniteLoopStatement
        while (true) {
            try {
                // 每秒刷新
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignore
            }
            // step1
            Set<String> timoutKeySet = new HashSet<>();
            clock.forEach((k, v) -> {
                if ((System.currentTimeMillis() / 1000) >= v) {
                    timoutKeySet.add(k);
                }
            });
            
            // step2 (不考虑step1和step2之间，时间刷新了的情况)
            for (String timeoutKey : timoutKeySet) {
                MemoryTransferMap.SINGLETON.getClock().remove(timeoutKey);
                MemoryTransferMap.SINGLETON.getGlobalMemory().remove(timeoutKey);
            }
        }
    }
    
    public Map<String, byte[]> getGlobalMemory() {
        return globalMemory;
    }
    
    public Map<String, Long> getClock() {
        return clock;
    }
    
    public AtomicBoolean getDaemonRunning() {
        return daemonRunning;
    }
    
}
